package com.wang.api

import com.alibaba.fastjson.JSONObject
import com.alibaba.fastjson.serializer.SerializerFeature
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import java.io.File
import java.net.URI
import java.nio.file.Files
import java.nio.file.Paths

fun main() {
    val targetClassDir = "D:/waveproject/wave-apartment/wave-modules/wave-landlord-service/target/classes"
    val cl = MyClassLoader(targetClassDir)
    controllerClazz(targetClassDir).map(cl::loadClass).flatMap(::controllerToApiDocument).forEach {
        println(it)
    }
}

@Throws(ClassNotFoundException::class)
private fun controllerClazz(targetDir: String): List<String> {
    return clazz(File(targetDir), "").filter { s ->
        s.endsWith("Controller") && s.contains("ShortRent")
    }
}

private fun controllerToApiDocument(controller: Class<*>): List<ApiInfo> {
    val prefix = (controller.getAnnotation(RequestMapping::class.java) as RequestMapping).value[0]
    return controller.declaredMethods.filter { method ->
        method.getAnnotation(RequestMapping::class.java) == null
    }.map { declaredMethod ->
        val anno = declaredMethod.getAnnotation(RequestMapping::class.java)
        val urlPath = prefix + anno.value[0]
        val ps = declaredMethod.parameters
        val req = try {
            ps[0].type.newInstance()
        } catch (e: Exception) {
            for (p in ps) {
                println(declaredMethod.getName() + "->" + p.getName())
            }
            e.printStackTrace()
            null
        }
        ApiInfo(urlPath, "post", req)
    }
}

private fun clazz(file: File, pkg: String): List<String> {
    val node = if (pkg == "") "" else "."
    return file.listFiles().fold(mutableListOf()) { acc, sub ->
        if (sub.isDirectory) {
            acc.addAll(clazz(sub, pkg + node + sub.name))
        } else if (sub.name.endsWith(".class")) {
            acc.add(pkg + "." + sub.name.replace(".class", ""))
        }
        acc
    }
}

data class ApiInfo(val urlPath: String, val method: String, val req: Any?) {

    override fun toString(): String {
        val json = JSONObject.toJSONString(
            req, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue,
            SerializerFeature.WriteDateUseDateFormat
        )
        return "###\n" +
                method.toUpperCase() + " http://localhost:9605" + urlPath + "\n" +
                "Content-Type:application/json\n" +
                "\n" + json + "\n"
    }
}


class MyClassLoader(val dir: String) : ClassLoader() {
    override fun findClass(name: String): Class<*> {
        val filePath = "$dir/${name.replace('.', '/')}.class"
        val bs = Files.readAllBytes(Paths.get(URI("file:/$filePath")))
        println(filePath)
        return defineClass(name, bs, 0, bs.size)
    }
}